/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.pz.net;

import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;

import java.util.HashMap;
import org.pz.net.shared.Command;
import static org.pz.net.util.BufferHelper.*;

/**
 *
 * @author Jannek
 */
public class Connection {
    private DatagramChannel channel;
    private int id;
    private int key;
    private boolean confirmedByMaster;
    private boolean isHost;
    
    private ByteBuffer data;
    private HashMap<Integer, Other> others;
    private Other server;
    
    protected Connection(int id, int key, boolean isHost) throws IOException {
        this.id = id;
        this.key = key;
        this.isHost = isHost;
        data = ByteBuffer.allocate(512);
        
        if (!isHost)
            others = new HashMap<Integer, Other>();
        else
            server = null;
        
        channel = DatagramChannel.open();
        channel.configureBlocking(false);
        
        retryLoop:
        for (long startTime = System.currentTimeMillis(); startTime + 10000 > System.currentTimeMillis();)
        {
            data.clear();
            put(Command.FROM_USER, data);
            data.putInt(id);
            data.putInt(key);
            data.flip();
            MasterServerLink.updateUdp(channel, data);
            
            waitForReplyLoop:
            for (long waitTime = System.currentTimeMillis(); waitTime + 1000 > System.currentTimeMillis();)
            {
                data.clear();
                if (channel.receive(data) != null)
                {
                    data.flip();
                    switch (getCommand(data))
                    {
                        case FROM_MASTER:
                            if (data.getInt() == key)
                            {
                                confirmedByMaster = true;
                                break retryLoop;
                            }
                            break waitForReplyLoop;
                        case ILLEGAL_ACTION:
                            break retryLoop;
                    }
                }
            }
        }
    }
    
    public boolean isUdpConfirmed()
    {
        return confirmedByMaster;
    }
    
    public void addMessage(Message message, int receiver)
    {
        Other to = getOther(receiver);
        if (to != null)
            to.enqueue(message);
    }
    
    private Other getOther(int otherId)
    {
        if (isHost)
        {
            if (others.containsKey(otherId))
                return others.get(otherId);
            return null;
        }
        return server;
    }
    
    protected void receive(long msTimeout) throws IOException
    {
        long endTime = System.nanoTime() + msTimeout * 1000000;
        SocketAddress address;
        Other from;
        data.clear();
        while ((address = channel.receive(data)) != null)
        {
            data.flip();
            switch (getCommand(data))
            {
                case FROM_USER_MULTI:
                case CONFIRM:
                case FROM_USER:
                case PING:
                case PONG:
                    from = getOther(data.getInt());
                    if (from != null)
                        from.receive(data, channel);
                    break;
                case FROM_MASTER:
                    switch (getCommand(data))
                    {
                        case NEW_NOTIFICATION:
                            
                    }
                    break;
            }
            
            if (System.nanoTime() > endTime)
                break;
            data.clear();
        }
    }
    
    protected void send() throws IOException
    {
        if (isHost)
            for (int i : others.keySet())
                others.get(i).send(channel);
        else
            server.send(channel);
    }
}
